package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParam;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;

import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
   @Autowired
    RestHighLevelClient client;

    @Override
    public PageResult search(RequestParam param) {

        //1.创建请求
        SearchRequest request = new SearchRequest("hotel");

        // 1. 准备bool query条件条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //设置请求参数
           //拿请求参数中的数
        String key = param.getKey();
        //判断搜索的关键字是否为空
        if (!StringUtils.isNotBlank(key)){
            //为空，则查询所有的酒店
            request.source().query(QueryBuilders.matchAllQuery());
        }else {
            //不为空则按搜索关键字查询
            boolQuery.filter(QueryBuilders.matchQuery("all",key));
        }
        //过滤条件
        if (StringUtils.isNotBlank(param.getCity())){
            boolQuery.filter(QueryBuilders.termQuery("city",param.getCity()));
        }
        if (StringUtils.isNotBlank(param.getBrand())){
            boolQuery.filter(QueryBuilders.termQuery("brand",param.getBrand()));
        }
        if (StringUtils.isNotBlank(param.getStarName())){
            boolQuery.filter(QueryBuilders.termQuery("starName",param.getStarName()));
        }
        if (param.getMinPrice()!=null && param.getMaxPrice() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(param.getMinPrice())
                                                                          .lte(param.getMaxPrice()));
        }
        //距离我附近
        String location = param.getLocation();
        if (StringUtils.isNotBlank(location)){
            request.source().sort(SortBuilders.geoDistanceSort("location",new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));
        }
        //算分控制
       /* FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(QueryBuilders.boolQuery(),
              //functionscore 数组
        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
               new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                    //过滤条件
                    QueryBuilders.termQuery("isAD",true) ,
                    //算分函数
                       ScoreFunctionBuilders.weightFactorFunction(10)
               )
        }
        );
     request.source().query(functionScoreQueryBuilder);*/
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                boolQuery, // 原来的bool查询条件
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ //  可以有多个算分函数
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(5) // 算分函数的得分
                        )
                }
        );
        request.source().query(functionScoreQueryBuilder);
        //分页
        Integer page = param.getPage();
        Integer size = param.getSize();
        request.source().from((page-1)*size).size(size);
        //2.发送请求
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析响应结果
            SearchHits hits = response.getHits();
            //有多少条数据
            long total = hits.getTotalHits().value;
            //存放文档的数组
            SearchHit[] hits1 = hits.getHits();
            List<HotelDoc> hotels = new ArrayList<>();
            for (SearchHit documentFields : hits1) {
                //获取文档的source字段
                String sourceAsString = documentFields.getSourceAsString();
                //反序列化，将json格式字符串转换为实体类对象
                HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
                //将数据存放到集合
                hotels.add(hotelDoc);
            }
            return new PageResult(total,hotels);
        } catch (IOException e) {
            e.printStackTrace();
            return new PageResult(0L,null);
        }

    }

    @Override
    public Map<String, List<String>> getFilters(RequestParam param) {
        //1.创建请求
        SearchRequest request = new SearchRequest("hotel");
        //设置参数
        // 1. 准备bool query条件条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //设置请求参数
        //拿请求参数中的数
        String key = param.getKey();
        //判断搜索的关键字是否为空
        if (!StringUtils.isNotBlank(key)){
            //为空，则查询所有的酒店
            request.source().query(QueryBuilders.matchAllQuery());
        }else {
            //不为空则按搜索关键字查询
            boolQuery.filter(QueryBuilders.matchQuery("all",key));
        }
        //过滤条件
        if (StringUtils.isNotBlank(param.getCity())){
            boolQuery.filter(QueryBuilders.termQuery("city",param.getCity()));
        }
        if (StringUtils.isNotBlank(param.getBrand())){
            boolQuery.filter(QueryBuilders.termQuery("brand",param.getBrand()));
        }
        if (StringUtils.isNotBlank(param.getStarName())){
            boolQuery.filter(QueryBuilders.termQuery("starName",param.getStarName()));
        }
        if (param.getMinPrice()!=null && param.getMaxPrice() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(param.getMinPrice())
                    .lte(param.getMaxPrice()));
        }
        //得到结果的
        request.source().size(0);
        //聚合
        builderAggreation(request);
        //发出请求
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析结果
            Map<String,List<String>> result = new HashMap<>();
            Aggregations aggregations = response.getAggregations();
            //根据名牌名称获取品牌结果
            List<String> brandList = getAggByName(aggregations,"brandAgg");
            // 根据城市名称得结果
            List<String> cityList = getAggByName(aggregations,"cityAgg");
            //根据星际，得结果
            List<String> starList = getAggByName(aggregations,"starAgg");
           //全部放map集合
            result.put("city",cityList);
            result.put("brand",brandList);
            result.put("starName",starList);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private List<String> getAggByName(Aggregations aggregations, String name) {
        //根据聚合名称获取聚合结果
        Terms brandTerm = aggregations.get(name);
        //获取buckers
        List<? extends Terms.Bucket> buckets = brandTerm.getBuckets();
        //遍历集合得到key
        List<String> brandList=new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            brandList.add(keyAsString);
        }
        return brandList;
    }

    //聚合方法
    public void builderAggreation(SearchRequest request){
        request.source().aggregation(AggregationBuilders.terms("brandAgg")
                                                          .field("brand")
                                                            .size(100));

        request.source().aggregation(AggregationBuilders.terms("cityAgg")
                .field("city")
                .size(100));
        request.source().aggregation(AggregationBuilders.terms("starAgg")
                .field("starName")
                .size(100));
    }

}
